class Snake {
  // 表示蛇头的元素
  head: HTMLElement;
  // 蛇的身体(包括蛇头)。HTMLCollection 是 HTML 元素的集合（会实时刷新，当添加新元素时，会自动补充新元素）
  bodies: HTMLCollection;
  // 获取蛇的容器
  element: HTMLElement;

  constructor() {
    this.element = document.getElementById("snake")!;
    this.head = document.querySelector("#snake > div") as HTMLElement;
    this.bodies = this.element.getElementsByTagName("div");
  }

  // 获取蛇的X坐标（其实就是蛇头的坐标）
  get X() {
    return this.head.offsetLeft;
  }

  // 获取蛇的Y坐标（其实就是蛇头的坐标）
  get Y() {
    return this.head.offsetTop;
  }

  // 设置蛇头的x坐标
  set X(value: number) {
    // 如果新值和旧值相同，则直接返回不再修改
    if(this.X === value) {
      return;
    }

    // X的值是否在合法范围0-290之间
    if(value < 0 || value > 290) {
      // 进入判断，说明蛇撞墙了
      throw new Error("蛇撞墙了");
      
    }
    
    // 这是是难点
    // 修改X时，是在修改水平坐标，设在左右移动，蛇在向左移动时，不能向右掉头，反之亦然
    if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
      // 如果放生了掉头，应该让蛇向反方向继续移动
      if(value > this.X) {
        value = this.X - 10;
      } else {
        value = this.X + 10;
      }
    }

    // 移动身体
    this.moveBody();

    this.head.style.left = value + "px";
    
    // 检查有没有撞到自己
    this.checkHeadBody();
  }

  // 设置蛇头的y坐标
  set Y(value: number) {
    // 如果新值和旧值相同，则直接返回不再修改
    if(this.Y === value) {
      return;
    }
    // Y的值是否在合法范围0-290之间
    if(value < 0 || value > 290) {
      // 进入判断，说明蛇撞墙了,抛出异常
      throw new Error("蛇撞墙了");
      
    }

    // 修改Y时，是在修改垂直坐标，蛇在上下移动，蛇在向上移动时，不能向下掉头，反之亦然
    if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
      // 如果放生了掉头，应该让蛇向反方向继续移动
      if(value > this.Y) {
        value = this.Y - 10;
      } else {
        value = this.Y + 10;
      }
    }

    // 移动身体
    this.moveBody();

    this.head.style.top = value + "px";

    // 检查有没有撞到自己
    this.checkHeadBody();
  }

  // 蛇增加身体的方法
  addBody() {
    // 向element最后添加一个div
    this.element.insertAdjacentHTML("beforeend", "<div></div>");
  }

  // 蛇身体移动的方法
  moveBody() {
    /**
     * 将后面的身体设置成前面身体的位置
     * 举例： 
     *      第四节 =  第三节的位置
     *      第三节 =  第二节的位置
     *      第二节 =  第一节的位置
     * **/
    // 遍历所有的身体
    for (let index = this.bodies.length - 1; index > 0; index--) {
      // 获取前面的身体
      let X = (this.bodies[index - 1] as HTMLElement).offsetLeft;
      let Y = (this.bodies[index - 1] as HTMLElement).offsetTop;

      // 将这个值设置到当前身体
      (this.bodies[index] as HTMLElement).style.left = X + "px";
      (this.bodies[index] as HTMLElement).style.top = Y + "px";
      
    }
  }


  // 检查蛇头和身体是否相撞
  checkHeadBody() {
    for (let index = 1; index < this.bodies.length; index++) {
      let bd = this.bodies[index] as HTMLElement;
      if(this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
        // 进入判断，说明蛇头撞到了身体，游戏结束
        throw new Error("撞到自己了");
      }
      
    }
  }
}

export default Snake;